#pragma once
#include <iostream>
#include <queue>
#include <pthread.h>
#include"Cond.hpp"
#include"Mutex.hpp"
namespace BlockQueueModule
{
    //vession 2
    using namespace CondModule;
    using namespace MutexModule;

    static const int gcap = 10;
    template<typename T>
    class BlockQueue
    {
        private:
        bool Full() { return _q.size() == _cap; }
        bool Empty() { return _q.empty(); }
        public:
        BlockQueue(int cap = gcap):_cap(cap),_cwaitnum(0),_pwaitnum(0)
        {}
        void Pop(T *out)
        {
            //加锁保护数据
            LockGuard lockguard(_mutex);
            //不能随便拿数据，队列空了就不能拿了
            while(Empty())//不能拿数据,让消费者去其条件下等待
            //用while进行条件判断可以在线程唤醒后在进行一次检查，防止线程伪唤醒
            {
                std::cout << "消费者进行等待..." << std::endl;
                _cwaitnum++;
                _consumer_cond.Wait(_mutex);//等待时会释放锁
                //当线程被唤醒时会重新申请锁（在临界区醒来）
                _cwaitnum--;
                std::cout << "消费者被唤醒..." << std::endl;
            }

            //队列不空 || 线程被唤醒，消费
            *out = _q.front();
            _q.pop();
            //完成消费，唤醒生产者
            if(_pwaitnum)
            {
                std::cout << "###唤醒生产者###" << std::endl;
                _productor_cond.Notify();
            }

        }
        void Equeue(T& in)
        {
            //加锁保护生产数据
            LockGuard lockguard(_mutex);
            //不能随便放数据，队列满了就不能放了

            while(Full())//不能放数据,让生产者去其条件下等待(在临界区等待)
            //用while进行条件判断可以在线程唤醒后在进行一次检查，防止线程伪唤醒
            {
                std::cout << "生产者进行等待..." << std::endl;
                _pwaitnum++;
                _productor_cond.Wait(_mutex);//等待时会释放锁
                //当线程被唤醒时会重新申请锁（在临界区醒来）
                _pwaitnum--;

                std::cout << "生产者被唤醒..." << std::endl;
            }
            //队列不满 || 线程被唤醒，生产
            _q.push(in);
            //完成生产，唤醒消费者
            if(_cwaitnum)
            {
                std::cout << "###唤醒消费者###" << std::endl;

                 _consumer_cond.Notify();
            }

        }
        ~BlockQueue()
        {}
        private:
        std::queue<T> _q;              //保存数据的容器
        int _cap;//                    //bq最大容量
        Mutex _mutex;        //锁，用于互斥
        Cond _productor_cond;//生产者条件变量
        Cond _consumer_cond; //消费者条件变量
        int _cwaitnum;                  //消费者等待数量
        int _pwaitnum;                  //生产者等待数量
    };


    // //vession 1
    // static const int gcap = 10;
    // template<typename T>
    // class BlockQueue
    // {
    //     private:
    //     bool Full() { return _q.size() == _cap; }
    //     bool Empty() { return _q.empty(); }
    //     public:
    //     BlockQueue(int cap = gcap):_cap(cap),_cwaitnum(0),_pwaitnum(0)
    //     {
    //         pthread_mutex_init(&_mutex, nullptr);
    //         pthread_cond_init(&_productor_cond,nullptr);
    //         pthread_cond_init(&_consumer_cond,nullptr);

    //     }
    //     void Pop(T *out)
    //     {
    //         //加锁保护数据
    //         pthread_mutex_lock(&_mutex);
    //         //不能随便拿数据，队列空了就不能拿了
    //         while(Empty())//不能拿数据,让消费者去其条件下等待
    //         //用while进行条件判断可以在线程唤醒后在进行一次检查，防止线程伪唤醒
    //         {
    //             std::cout << "消费者进行等待..." << std::endl;
    //             _cwaitnum++;
    //             pthread_cond_wait(&_consumer_cond,&_mutex);//等待时会释放锁
    //             //当线程被唤醒时会重新申请锁（在临界区醒来）
    //             _cwaitnum--;
    //             std::cout << "消费者被唤醒..." << std::endl;
    //         }

    //         //队列不空 || 线程被唤醒，消费
    //         *out = _q.front();
    //         _q.pop();
    //         //完成消费，唤醒生产者
    //         if(_pwaitnum)
    //         {
    //             std::cout << "###唤醒生产者###" << std::endl;
    //             pthread_cond_signal(&_productor_cond);
    //         }
    //         pthread_mutex_unlock(&_mutex);

    //     }
    //     void Equeue(T& in)
    //     {
    //         //加锁保护生产数据
    //         pthread_mutex_lock(&_mutex);

    //         //不能随便放数据，队列满了就不能放了

    //         while(Full())//不能放数据,让生产者去其条件下等待(在临界区等待)
    //         //用while进行条件判断可以在线程唤醒后在进行一次检查，防止线程伪唤醒
    //         {
    //             std::cout << "生产者进行等待..." << std::endl;
    //             _pwaitnum++;
    //             pthread_cond_wait(&_productor_cond,&_mutex);//等待时会释放锁
    //             //当线程被唤醒时会重新申请锁（在临界区醒来）
    //             _pwaitnum--;

    //             std::cout << "生产者被唤醒..." << std::endl;
    //         }
    //         //队列不满 || 线程被唤醒，生产
    //         _q.push(in);
    //         //完成生产，唤醒消费者
    //         if(_cwaitnum)
    //         {
    //             std::cout << "###唤醒消费者###" << std::endl;

    //             pthread_cond_signal(&_consumer_cond);
    //         }
    //         pthread_mutex_unlock(&_mutex);
    //     }
    //     ~BlockQueue()
    //     {
    //         pthread_mutex_destroy(&_mutex);
    //         pthread_cond_destroy(&_productor_cond);
    //         pthread_cond_destroy(&_consumer_cond);

    //     }
    //     private:
    //     std::queue<T> _q;              //保存数据的容器
    //     int _cap;//                    //bq最大容量
    //     pthread_mutex_t _mutex;        //锁，用于互斥
    //     pthread_cond_t _productor_cond;//生产者条件变量
    //     pthread_cond_t _consumer_cond; //消费者条件变量
    //     int _cwaitnum;                  //消费者等待数量
    //     int _pwaitnum;                  //生产者等待数量
    // };
}